home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK1.toast / Development Kits (Disc 1) / Apple Shared Library Manager / ASLM Examples / Sample Apps / PSample / Sources / Sample.p < prev    next >
Encoding:
Text File  |  1996-11-19  |  24.9 KB  |  770 lines  |  [TEXT/MPS ]

  1. {
  2.     File:        Sample.p
  3.  
  4.     Contains:    This is a simple application, based on the DTS traffic light example,
  5.                 that demonstrates how to use the Shared Library Manager. It puts the
  6.                 functionality of the traffic light window into a shared library and
  7.                 then uses that library from the client application.
  8.  
  9.     Copyright:    © 1993-1994 by Apple Computer, Inc., all rights reserved.
  10.  
  11. }
  12.  
  13.  
  14. PROGRAM Sample;
  15.  
  16. USES
  17.     MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, Traps, Strings, PasLibIntf,
  18.     SampleLibrary, LibraryManager, LibraryManagerUtilities;
  19.  
  20. CONST
  21.     kSysEnvironsVersion        = 1;
  22.     kOSEvent                = app4Evt;    {event used by MultiFinder}
  23.     kSuspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  24.     kResumeMask                = 1;        {bit of message field for resume vs. suspend}
  25.     kNoEvents                = 0;        {no events mask}
  26.      
  27.     kMinHeap    = 21 * 1024;
  28.          
  29.     kMinSpace    = 8 * 1024;
  30.     
  31.     {kExtremeNeg and kExtremePos are used to set up wide open rectangles and regions.}
  32.     kExtremeNeg    = -32768;
  33.     kExtremePos    = 32767 - 1;            {required for old region bug}
  34.     
  35.     {The following constants are all resource IDs, corresponding to resources in Sample.r.}
  36.     rMenuBar    = 128;                    {application's menu bar}
  37.     rAboutAlert    = 128;                    {about alert}
  38.     rUserAlert    = 129;                    {error user alert}
  39.  
  40.     {The following constants are used to identify menus and their items. The menu IDs
  41.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  42.     mApple        = 128;                    {Apple menu}
  43.     iAbout        = 1;
  44.  
  45.     mFile        = 129;                    {File menu}
  46.     iNew        = 1;
  47.     iClose        = 4;
  48.     iQuit        = 12;
  49.  
  50.     mEdit        = 130;                    {Edit menu}
  51.     iUndo        = 1;
  52.     iCut        = 3;
  53.     iCopy        = 4;
  54.     iPaste        = 5;
  55.     iClear        = 6;
  56.     
  57.     kDITop        = $0050;
  58.     kDILeft        = $0070;
  59.     
  60. VAR
  61.     {The "g" prefix is used to emphasize that a variable is global.}
  62.  
  63.     {gMac is used to hold the result of a SysEnvirons call. This makes
  64.      it convenient for any routine to check the environment. It is
  65.      global information, anyway.}
  66.  
  67.     gMac                : SysEnvRec;    {set up by Initialize}
  68.  
  69.     {gHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
  70.      trap is available. If it is false, we know that we must call GetNextEvent.}
  71.  
  72.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  73.  
  74.     {gInBackground is maintained by our osEvent handling routines. Any part of
  75.      the program can check it to find out if it is currently in the background.}
  76.  
  77.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  78.  
  79.  
  80. (*————————————————————————————————————————————————————————————————————————————————————
  81.     TrapAvailable
  82.     
  83.     Check to see if a given trap is implemented. This is only used by the initialize 
  84.     routine in this program, so we put it in the initialize segment.
  85.  
  86. ————————————————————————————————————————————————————————————————————————————————————*)
  87.  
  88. {$S Initialize}
  89. FUNCTION MyTrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  90.  
  91. BEGIN
  92.     IF (tType = ToolTrap) &
  93.         (gMac.machineType > envMachUnknown) &
  94.         (gMac.machineType < envMacII) THEN BEGIN        {it's a 512KE, Plus, or SE}
  95.         tNumber := BAND(tNumber, $03FF);
  96.         IF tNumber > $01FF THEN                            {which means the tool traps}
  97.             tNumber := _Unimplemented;                    {only go to $01FF}
  98.     END;
  99.     MyTrapAvailable := NGetTrapAddress(tNumber, tType) <>
  100.                         NGetTrapAddress(_Unimplemented, tType);
  101. END; {MyTrapAvailable}
  102.  
  103. (*————————————————————————————————————————————————————————————————————————————————————
  104.     IsDAWindow
  105.     
  106.     Check if a window belongs to a desk accessory
  107.  
  108. ————————————————————————————————————————————————————————————————————————————————————*)
  109.  
  110. {$S Main}
  111. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  112.  
  113. BEGIN
  114.     IF window = NIL THEN
  115.         IsDAWindow := FALSE
  116.     ELSE    {DA windows have negative windowKinds}
  117.         IsDAWindow := (WindowPeek(window)^.windowKind < 0);
  118. END; {IsDAWindow}
  119.  
  120. (*————————————————————————————————————————————————————————————————————————————————————
  121.     IsAppWindow
  122.     
  123.     Check to see if a window belongs to the application. 
  124.  
  125. ————————————————————————————————————————————————————————————————————————————————————*)
  126.  
  127. {$S Main}
  128. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  129.  
  130. BEGIN
  131.     IF window = NIL THEN
  132.         IsAppWindow := FALSE
  133.     ELSE    {application windows have windowKinds = userKind (8)}
  134.         WITH WindowPeek(window)^ DO
  135.             IsAppWindow := (windowKind = userKind);
  136. END; {IsAppWindow}
  137.  
  138. (*————————————————————————————————————————————————————————————————————————————————————
  139.     AlertUser
  140.     
  141.     Display an alert that tells the user an error occured, then exit the program. This
  142.     routine is used as an ultimate bail-out for serious errors that prohibit the cont-
  143.     inuation of the application.
  144.  
  145. ————————————————————————————————————————————————————————————————————————————————————*)
  146.  
  147. {$S Main}
  148. PROCEDURE AlertUser;
  149.  
  150. VAR
  151.     itemHit    : INTEGER;
  152. BEGIN
  153.     SetCursor(arrow);
  154.     itemHit := Alert(rUserAlert, NIL);
  155.     ExitToShell;
  156. END; {AlertUser}
  157.  
  158. (*————————————————————————————————————————————————————————————————————————————————————
  159.     DoCloseWindow
  160.     
  161.     Close a window. This handles desk accessory and application windows. Note the
  162.     traffic library manager allocated the windowrecord for window, so disposing of
  163.     the WindowRecord should be left upon the traffic light library.
  164.  
  165. ————————————————————————————————————————————————————————————————————————————————————*)
  166.  
  167. {$S Main}
  168. FUNCTION DoCloseWindow(window: WindowPtr) : BOOLEAN;
  169.  
  170. BEGIN
  171.  
  172.     DoCloseWindow := TRUE;
  173.     IF IsDAWindow(window) THEN
  174.         CloseDeskAcc(WindowPeek(window)^.windowKind)
  175.     ELSE IF IsAppWindow(window) THEN
  176.         FreeTrafficLight;                    { give a chance for our library to clean }
  177.  
  178.  
  179. END; {DoCloseWindow}
  180.  
  181. (*————————————————————————————————————————————————————————————————————————————————————
  182.     UnLoadTrafficLightLibrary
  183.     
  184.     unload the traffic light object and free the memory allocated by our library
  185.  
  186. ————————————————————————————————————————————————————————————————————————————————————*)
  187.  
  188. PROCEDURE UnLoadTrafficLightLibrary;
  189. VAR
  190.     err                : OSErr;
  191.     str                : Str255;
  192.     pstr            : StringPtr;
  193. BEGIN
  194.  
  195.     (* need to convert the string from pascal to C, since FunctionSetID is a C string *)
  196.     
  197.     pstr := PLStrCpy(str, kTrafficLightFunctionSet);
  198.     P2CStrProc(@str[0]);
  199.     err:= UnloadFunctionSet(@str[0]);
  200.                                              { A library loaded with LoadFunctionSet() is not
  201.                                               unloaded until all objects from the library
  202.                                               are deleted and an UnloadFunctionSet() call is 
  203.                                               made to balance each LoadFunctionSet() call }
  204.     CleanupLibraryManager;                    { we are done with library manager }
  205.     ExitToShell;
  206.  
  207. END; { UnLoadTrafficLightLibrary }
  208.  
  209. (*————————————————————————————————————————————————————————————————————————————————————
  210.     LoadTrafficLightLibrary
  211.     
  212.     load the traffic light library. This library automatically creates a window and
  213.     attach a traffic menu to our current menu.
  214.  
  215. ————————————————————————————————————————————————————————————————————————————————————*)
  216.  
  217. {$S Initialize}
  218. PROCEDURE LoadTrafficLightLibrary( window : WindowPtr );
  219. VAR
  220.     loaded        : BOOLEAN;
  221.     str                : Str255;
  222.     pstr            : StringPtr;
  223. BEGIN
  224.  
  225.     (* always initilize the library manager before any Shared Library calls. This
  226.        creates a local instance of TLibraryManager for accessing our libraries. *)
  227.  
  228.     IF( InitLibraryManager( 0,                 { we don't need memory in our local pool }
  229.                             kCurrentZone,     { use application zone = current zone }
  230.                             kNormalMemory )    { default memory type, no VM stuff }
  231.                             <> kNoError ) THEN
  232.         AlertUser;                            { failed to load the library, tell user } 
  233.  
  234.     (* need to convert the string from pascal to C, since FunctionSetID is a C string *)
  235.     pstr := PLStrCpy(str, kTrafficLightFunctionSet);
  236.     P2CStrProc(@str[0]);
  237.  
  238.     (* make sure the library is available before initializing trafficlight(via LoadFunctionSet) *)
  239.     loaded := LoadFunctionSet(@str[0], TRUE) = kNoError;
  240.     IF( loaded ) THEN BEGIN                {if loaded continue}
  241.         IF ( NewTrafficLight <> noErr ) THEN{ initialize our trafficlight shared library }
  242.             AlertUser;                        { !oh well, we tried }
  243.     END
  244.     ELSE
  245.         AlertUser;
  246.  
  247. END; {LoadTrafficLightLibrary}
  248.  
  249. (*————————————————————————————————————————————————————————————————————————————————————
  250.     Initialize
  251.     
  252.     Set up the whole world, including global variables, Toolbox managers,
  253.     and menus. Sample is going to create its window, and traffic menu through its
  254.     traffic library, using the shared library manager(Nahhhh...). The Library auto-
  255.     matically create a window, attach a menu and draws the traffic light.
  256.  
  257. ————————————————————————————————————————————————————————————————————————————————————*)
  258.  
  259. {$S Initialize}
  260. PROCEDURE Initialize;
  261.  
  262. VAR
  263.     menuBar            : Handle;
  264.     window            : WindowPtr;
  265.     ignoreError        : OSErr;
  266.     total, contig    : LongInt;
  267.     ignoreResult    : BOOLEAN;
  268.     event            : EventRecord;
  269.     count            : INTEGER;
  270.  
  271. BEGIN
  272.     gInBackground := FALSE;
  273.  
  274.     InitGraf(@thePort);
  275.     InitFonts;
  276.     InitWindows;
  277.     InitMenus;
  278.     TEInit;
  279.     InitDialogs(NIL);
  280.     InitCursor;
  281.          
  282.     {This next bit of code is necessary to allow the default button of our
  283.      alert be outlined }
  284.  
  285.     FOR count := 1 TO 3 DO
  286.         ignoreResult := EventAvail(everyEvent, event);
  287.          
  288.     {Ignore the error returned from SysEnvirons; even if an error occurred,
  289.      the SysEnvirons glue will fill in the SysEnvRec. You can save a redundant
  290.      call to SysEnvirons by calling it after initializing AppleTalk.}
  291.  
  292.     ignoreError := SysEnvirons(kSysEnvironsVersion, gMac);
  293.     
  294.     {Make sure that the machine has at least 128K ROMs. If it doesn't, exit.}
  295.     
  296.     IF gMac.machineType < 0 THEN AlertUser;
  297.          
  298.     {Move MyTrapAvailable call to after SysEnvirons so that we can tell
  299.      in MyTrapAvailable if a tool trap value is out of range.}
  300.  
  301.     gHasWaitNextEvent := MyTrapAvailable(_WaitNextEvent, ToolTrap);
  302.  
  303.     {We used to make a check for memory at this point by examining ApplLimit,
  304.      ApplicationZone, and StackSpace and comparing that to the minimum size we told
  305.      MultiFinder we needed. This did not work well because it assumed too much about
  306.      the relationship between what we asked MultiFinder for and what we would actually
  307.      get back, as well as how to measure it. Instead, we will use an alternate
  308.      method comprised of two steps.}
  309.      
  310.     {It is better to first check the size of the application heap against a value
  311.      that you have determined is the smallest heap the application can reasonably
  312.      work in. This number should be derived by examining the size of the heap that
  313.      is actually provided by MultiFinder when the minimum size requested is used.
  314.      The derivation of the minimum size requested from MultiFinder is described
  315.      in Sample.h. The check should be made because the preferred size can end up
  316.      being set smaller than the minimum size by the user. This extra check acts to
  317.      insure that your application is starting from a solid memory foundation.}
  318.      
  319.     IF ORD(GetApplLimit) - ORD(ApplicationZone) < kMinHeap THEN AlertUser;
  320.     
  321.     {Next, make sure that enough memory is free for your application to run. It
  322.      is possible for a situation to arise where the heap may have been of required
  323.      size, but a large scrap was loaded which left too little memory. To check for
  324.      this, call PurgeSpace and compare the result with a value that you have determined
  325.      is the minimum amount of free memory your application needs at initialization.
  326.      This number can be derived several different ways. One way that is fairly
  327.      straightforward is to run the application in the minimum size configuration
  328.      as described previously. Call PurgeSpace at initialization and examine the value
  329.      returned. However, you should make sure that this result is not being modified
  330.      by the scrap's presence. You can do that by calling ZeroScrap before calling
  331.      PurgeSpace. Make sure to remove that call before shipping, though.}
  332.  
  333.     PurgeSpace(total, contig);
  334.     IF total < kMinSpace THEN AlertUser;
  335.  
  336.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  337.     IF menuBar = NIL THEN AlertUser;
  338.     SetMenuBar(menuBar);                    {install menus}
  339.     DisposeHandle(menuBar);
  340.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  341.     DrawMenuBar;
  342.     
  343.     LoadTrafficLightLibrary( window );        {Load trafficlight shared library}
  344.  
  345. END; {Initialize}
  346.  
  347. (*————————————————————————————————————————————————————————————————————————————————————
  348.     Terminate
  349.     
  350.     Cleanup the application and exits. Close all of the windows, and cleanup our 
  351.     library. We replace the ExitToShell by UnLoadTrafficLightLibrary so we get a 
  352.     chance to unload the library and cleanup the library manager.
  353.  
  354. ————————————————————————————————————————————————————————————————————————————————————*)
  355.  
  356.  
  357. {$S Main}
  358. PROCEDURE Terminate;
  359.  
  360. VAR
  361.     aWindow    : WindowPtr;
  362.     closed    : BOOLEAN;
  363.  
  364. BEGIN
  365.     closed := TRUE;
  366.     REPEAT
  367.         aWindow := FrontWindow;                    {get the current front window}
  368.         IF aWindow <> NIL THEN
  369.             closed := DoCloseWindow(aWindow);    {close this window}
  370.     UNTIL (NOT closed) | (aWindow = NIL);        {do all windows}
  371.     IF closed THEN
  372.         UnLoadTrafficLightLibrary;                    {exit if no cancellation}
  373. END; {Terminate}
  374.  
  375. (*————————————————————————————————————————————————————————————————————————————————————
  376.     AdjustMenus
  377.     
  378.     Enable and disable menus based on the current state. The user can only select en-
  379.     abled menu items. We set up all the menu items  before calling MenuSelect or MenuKey,
  380.     since these are the only times that  a menu item can be selected. Note that MenuSelect
  381.     is also the only time the user will see menu items. This approach to deciding what 
  382.     enable/disable state a menu item has the advantage of concentrating all the decision-
  383.      making in one routine, as opposed to being spread throughout the application. Other
  384.     application designs may take a different approach that may or may not be just as valid.}
  385.  
  386. ————————————————————————————————————————————————————————————————————————————————————*)
  387.  
  388. {$S Main}
  389. PROCEDURE AdjustMenus;
  390.  
  391. VAR
  392.     window            : WindowPtr;
  393.     menu            : MenuHandle;
  394.  
  395. BEGIN
  396.     window := FrontWindow;
  397.  
  398.     menu := GetMenuHandle(mFile);
  399.     IF IsDAWindow(window) THEN                {we can allow desk accessories to be closed from the menu}
  400.         EnableItem(menu, iClose)
  401.     ELSE
  402.         DisableItem(menu, iClose);            {but not our traffic light window}
  403.  
  404.     menu := GetMenuHandle(mEdit);
  405.     IF IsDAWindow(window) THEN BEGIN        {a desk accessory might need the edit menu}
  406.         EnableItem(menu, iUndo);
  407.         EnableItem(menu, iCut);
  408.         EnableItem(menu, iCopy);
  409.         EnableItem(menu, iPaste);
  410.         EnableItem(menu, iClear);
  411.     END ELSE BEGIN                            {but we know we do not}
  412.         DisableItem(menu, iUndo);
  413.         DisableItem(menu, iCut);
  414.         DisableItem(menu, iCopy);
  415.         DisableItem(menu, iClear);
  416.         DisableItem(menu, iPaste);
  417.     END;
  418.  
  419.     (* Since our library takes also care of its own menu, we call it through the Function-
  420.        set AdustTrafficMenus *)
  421.      
  422.     AdjustTrafficLightMenus( IsAppWindow(window) );
  423.     
  424. END; {AdjustMenus}
  425.  
  426. (*————————————————————————————————————————————————————————————————————————————————————
  427.     DoMenuCommand
  428.     
  429.     This is called when an item is chosen from the menu bar( after calling MenuSelect
  430.     or MenuKey). It performs the right operation for each command.  It is good to have
  431.     both the result of MenuSelect and MenuKey go to one routine like this to keep 
  432.     everything organized.
  433.  
  434. ————————————————————————————————————————————————————————————————————————————————————*)
  435.  
  436. {$S Main}
  437. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  438.  
  439. VAR
  440.     menuID            : INTEGER;        {the resource ID of the selected menu}
  441.     menuItem        : INTEGER;        {the item number of the selected menu}
  442.     itemHit            : INTEGER;
  443.     savedrefnum        : INTEGER;
  444.     daName            : Str255;
  445.     daRefNum        : INTEGER;
  446.     handledByDA        : BOOLEAN;
  447.     ignore            : BOOLEAN;
  448. BEGIN
  449.  
  450.     menuID := HighWord(menuResult);    {use built-ins (for efficiency)...}
  451.     menuItem := LowWord(menuResult);{to get menu item number and menu number}
  452.  
  453.     CASE menuID OF
  454.         mApple:
  455.             CASE menuItem OF
  456.                 iAbout:                {bring up alert for About}
  457.                     itemHit := Alert(rAboutAlert, NIL);
  458.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  459.                     GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);
  460.                     daRefNum := OpenDeskAcc(daName);
  461.                 END;
  462.             END;
  463.         mFile:
  464.             CASE menuItem OF
  465.                 iClose:
  466.                     ignore := DoCloseWindow(FrontWindow); {we don't care if cancelled}
  467.                 iQuit:
  468.                     Terminate;
  469.             END;
  470.         mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  471.             handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  472.         mLight:
  473.         BEGIN
  474.             (* now its time to adjust our shared library menus *)
  475.             DoTrafficLightMenuCommand( menuItem );
  476.         END;
  477.     END;
  478.     HiliteMenu(0);                    {unhighlight what MenuSelect (or MenuKey) hilited}
  479. END; {DoMenuCommand}
  480.  
  481. (*————————————————————————————————————————————————————————————————————————————————————
  482.     DrawWindow
  483.     
  484.     The draw window is reduced to a single call to our library. Our library is res-
  485.     ponsible for drawing the traffic light.
  486.  
  487. ————————————————————————————————————————————————————————————————————————————————————*)
  488.  
  489. {$S Main}
  490. PROCEDURE DrawWindow(window: WindowPtr);
  491.  
  492. BEGIN
  493.  
  494.     DrawLight;                                    { Draw traffic light through library}
  495.         
  496. END; {DrawWindow}
  497.  
  498.  
  499. (*————————————————————————————————————————————————————————————————————————————————————
  500.     DoContentClick
  501.  
  502.     This is called when a mouse-down event occurs in the content of a window.
  503.     Other applications might want to call FindControl, TEClick, etc., to
  504.     further process the click.
  505.  
  506. ————————————————————————————————————————————————————————————————————————————————————*)
  507. {$S Main}
  508. PROCEDURE DoContentClick(window: WindowPtr; event: EventRecord);
  509.  
  510. BEGIN
  511.  
  512.     (* Here we are going to toggle the traffic light by getting the previous state 
  513.        and inverting it to new state. All the calls are going through FunctionSet *)
  514.  
  515.     SetLightState( NOT GetLightState );
  516.  
  517. END; {DoContentClick}
  518.  
  519. (*————————————————————————————————————————————————————————————————————————————————————
  520.     DoUpdate
  521.     
  522.     This is called when an update event is received for a window.
  523.     It calls DrawWindow to draw the contents of an application window.
  524.     As an effeciency measure that does not have to be followed, it
  525.     calls the drawing routine only if the visRgn is non-empty. This
  526.     will handle situations where calculations for drawing or drawing
  527.     itself is very time-consuming.
  528.  
  529. ————————————————————————————————————————————————————————————————————————————————————*)
  530.  
  531. {$S Main}
  532. PROCEDURE DoUpdate(window: WindowPtr);
  533.  
  534. BEGIN
  535.     IF IsAppWindow(window) THEN BEGIN
  536.         BeginUpdate(window);                    {sets up the visRgn, clears updateRgn}
  537.         IF NOT EmptyRgn(window^.visRgn) THEN    {draw if updating needs to be done}
  538.             DrawWindow(window);
  539.         EndUpdate(window);                        {restores the visRgn}
  540.     END;
  541. END; {DoUpdate}
  542.  
  543. (*————————————————————————————————————————————————————————————————————————————————————
  544.     DoActivate
  545.     
  546.     Responsible for activating and deactivating window and its contents.
  547.  
  548. ————————————————————————————————————————————————————————————————————————————————————*)
  549.  
  550. {$S Main}
  551. PROCEDURE DoActivate(window: WindowPtr; becomingActive: BOOLEAN);
  552.  
  553. BEGIN
  554.     IF IsAppWindow(window) THEN
  555.         IF becomingActive THEN
  556.             {do whatever you need to at activation}
  557.         ELSE
  558.             {do whatever you need to at deactivation};
  559. END; {DoActivate}
  560.  
  561. (*————————————————————————————————————————————————————————————————————————————————————
  562.     GetGlobalMouse
  563.     
  564.     Get the global coordinates of the mouse.  The coordinate of the mouse is used to
  565.     adjust the cursor accordingly.
  566.  
  567. ————————————————————————————————————————————————————————————————————————————————————*)
  568.  
  569.  
  570. {$S Main}
  571. PROCEDURE GetGlobalMouse(VAR mouse: Point);
  572.  
  573. VAR
  574.     event    : EventRecord;
  575.     
  576. BEGIN
  577.     IF OSEventAvail(kNoEvents, event) THEN;    {we aren't interested in any events}
  578.     mouse := event.where;                    {just the mouse position}
  579. END;
  580.  
  581. (*————————————————————————————————————————————————————————————————————————————————————
  582.     AdjustCursor
  583.     
  584.     Change the cursor's shape, depending on its position. This also calculates the 
  585.     region where the current cursor resides (for WaitNextEvent).  If the mouse is ever
  586.     outside of that region, an event is generated, causing this routine to be called.
  587.  
  588. ————————————————————————————————————————————————————————————————————————————————————*)
  589.  
  590. {$S Main}
  591. PROCEDURE AdjustCursor(mouse: Point; region: RgnHandle);
  592.  
  593. VAR
  594.     window                : WindowPtr;
  595.     arrowRgn            : RgnHandle;
  596.     plusRgn                : RgnHandle;
  597.     globalPortRect        : Rect;
  598.     
  599.  
  600. BEGIN
  601.     window := FrontWindow;    {we only adjust the cursor when we are in front}
  602.     IF (NOT gInBackground) AND (NOT IsDAWindow(window)) THEN BEGIN
  603.         {calculate regions for different cursor shapes}
  604.         arrowRgn := NewRgn;
  605.         plusRgn := NewRgn;
  606.  
  607.         {start with a big, big rectangular region}
  608.         {1.01 - changed to kExtremeNeg and kExtremePos for consistency}
  609.         SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg,
  610.                             kExtremePos, kExtremePos);
  611.  
  612.         {calculate plusRgn}
  613.         IF IsAppWindow(window) THEN BEGIN
  614.             SetPort(window);            {make a global version of the portRect}
  615.             SetOrigin(-window^.portBits.bounds.left, -window^.portBits.bounds.top);
  616.             globalPortRect := window^.portRect;
  617.             RectRgn(plusRgn, globalPortRect);
  618.             SectRgn(plusRgn, window^.visRgn, plusRgn);
  619.             SetOrigin(0, 0);
  620.         END;
  621.  
  622.         {subtract other regions from arrowRgn}
  623.         DiffRgn(arrowRgn, plusRgn, arrowRgn);
  624.  
  625.         {change the cursor and the region parameter}
  626.         IF PtInRgn(mouse, plusRgn) THEN BEGIN
  627.             SetCursor(GetCursor(plusCursor)^^);
  628.             CopyRgn(plusRgn, region);
  629.         END ELSE BEGIN
  630.             SetCursor(arrow);
  631.             CopyRgn(arrowRgn, region);
  632.         END;
  633.  
  634.         {get rid of our local regions}
  635.         DisposeRgn(arrowRgn);
  636.         DisposeRgn(plusRgn);
  637.     END;
  638. END; {AdjustCursor}
  639.  
  640. (*————————————————————————————————————————————————————————————————————————————————————
  641.     DoEvent
  642.     
  643.     Do the right thing for an event. Determine what kind of event it is, and call the
  644.     appropriate routine.
  645.  
  646. ————————————————————————————————————————————————————————————————————————————————————*)
  647.  
  648. {$S Main}
  649. PROCEDURE DoEvent(event: EventRecord);
  650.  
  651. VAR
  652.     part, err    : INTEGER;
  653.     window        : WindowPtr;
  654.     hit            : BOOLEAN;
  655.     key            : CHAR;
  656.     aPoint        : Point;
  657.  
  658. BEGIN
  659.     CASE event.what OF
  660.         mouseDown: BEGIN
  661.             part := FindWindow(event.where, window);
  662.             CASE part OF
  663.                 inMenuBar: BEGIN            {process the menu command}
  664.                     AdjustMenus;
  665.                     DoMenuCommand(MenuSelect(event.where));
  666.                 END;
  667.                 inSysWindow:                {let the system handle the mouseDown}
  668.                     SystemClick(event, window);
  669.                 inContent:
  670.                     IF window <> FrontWindow THEN BEGIN
  671.                         SelectWindow(window);
  672.                         {DoEvent(event);}    {use this line for "do first click"}
  673.                     END ELSE
  674.                         DoContentClick(window, event);
  675.                 inDrag:                        {pass screenBits.bounds to get all gDevices}
  676.                     DragWindow(window, event.where, screenBits.bounds);
  677.                 inGrow:;
  678.                 inZoomIn, inZoomOut:;
  679.             END;
  680.         END;
  681.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  682.             key := CHR(BAnd(event.message, charCodeMask));
  683.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN    {Command key down}
  684.                 IF event.what = keyDown THEN BEGIN
  685.                     AdjustMenus;            {enable/disable/check menu items properly}
  686.                     DoMenuCommand(MenuKey(key));
  687.                 END;
  688.         END;                                {call DoActivate with the window and...}
  689.         activateEvt:                        {TRUE for activate, FALSE for deactivate}
  690.             DoActivate(WindowPtr(event.message), BAnd(event.modifiers, activeFlag) <> 0);
  691.         updateEvt:                          {call DoUpdate with the window to update}
  692.             DoUpdate(WindowPtr(event.message));
  693.         {1.01 - It is not a bad idea to at least call DIBadMount in response
  694.          to a diskEvt, so that the user can format a floppy.}
  695.         diskEvt:
  696.             IF HighWord(event.message) <> noErr THEN BEGIN
  697.                 SetPt(aPoint, kDILeft, kDITop);
  698.                 err := DIBadMount(aPoint, event.message);
  699.             END;
  700.         kOSEvent:
  701.             CASE BAnd(BRotL(event.message, 8), $FF) OF    {high byte of message}
  702.                 kSuspendResumeMessage: BEGIN
  703.                     gInBackground := BAnd(event.message, kResumeMask) = 0;
  704.                     DoActivate(FrontWindow, NOT gInBackground);
  705.                 END;
  706.             END;
  707.     END;
  708. END; {DoEvent}
  709.  
  710. (*————————————————————————————————————————————————————————————————————————————————————
  711.     EventLoop
  712.     
  713.     Get events forever, and handle them by calling DoEvent. Get the events by calling
  714.     WaitNextEvent, if it's available, otherwise by calling GetNextEvent. Also call 
  715.     AdjustCursor each time through the loop.
  716.  
  717. ————————————————————————————————————————————————————————————————————————————————————*)
  718.  
  719. {$S Main}
  720. PROCEDURE EventLoop;
  721.  
  722. VAR
  723.     cursorRgn    : RgnHandle;
  724.     gotEvent    : BOOLEAN;
  725.     event        : EventRecord;
  726.     mouse        : Point;
  727.  
  728. BEGIN
  729.     cursorRgn := NewRgn;                {we’ll pass WNE an empty region the 1st time thru}
  730.     REPEAT
  731.         IF gHasWaitNextEvent THEN BEGIN    {put us 'asleep' forever under MultiFinder}
  732.             GetGlobalMouse(mouse);        {since we might go to sleep}
  733.             AdjustCursor(mouse, cursorRgn);
  734.             gotEvent := WaitNextEvent(everyEvent, event, MAXLONGINT, cursorRgn);
  735.         END ELSE BEGIN
  736.             SystemTask;                    {must be called if using GetNextEvent}
  737.             gotEvent := GetNextEvent(everyEvent, event);
  738.         END;
  739.         IF gotEvent THEN BEGIN
  740.             AdjustCursor(event.where, cursorRgn);    {make sure we have the right cursor}
  741.             DoEvent(event);
  742.         END;
  743.         {If you are using modeless dialogs that have editText items,
  744.          you will want to call IsDialogEvent to give the caret a chance
  745.          to blink, even if WNE/GNE returned FALSE. However, check FrontWindow
  746.          for a non-NIL value before calling IsDialogEvent.}
  747.     UNTIL FALSE;                        {loop forever; we quit through an ExitToShell}
  748. END; {EventLoop}
  749.  
  750. (*————————————————————————————————————————————————————————————————————————————————————
  751.     _DataInit
  752.     
  753.     This routine is part of the MPW runtime library. This external reference to it
  754.     is done so that we can unload its segment, %A5Init.
  755. ————————————————————————————————————————————————————————————————————————————————————*)
  756.  
  757. PROCEDURE _DataInit; EXTERNAL;
  758.  
  759. {$S Main}
  760. BEGIN
  761.     UnloadSeg(@_DataInit);    {note that _DataInit must not be in Main!}
  762.          
  763.     MaxApplZone;            {expand the heap so code segments load at the top}
  764.  
  765.     Initialize;                {initialize the program}
  766.     UnloadSeg(@Initialize);    {note that Initialize must not be in Main!}
  767.  
  768.     EventLoop;                {call the main event loop}
  769. END.
  770.